package com.icee.myth.server.base.occupy;

import com.icee.myth.common.message.serverMessage.Message;
import com.icee.myth.common.messageQueue.DBMessageQueue;
import com.icee.myth.common.messageQueue.ServerMessageQueue;
import com.icee.myth.log.GameLogger;
import com.icee.myth.log.message.FileDebugGameLogMessage;
import com.icee.myth.log.message.builder.GameLogMessageBuilder;
import com.icee.myth.server.GameServer;
import com.icee.myth.server.actor.Human;
import com.icee.myth.server.base.BaseTarget;
import com.icee.myth.server.index.PlayerIdsOfLevel;
import com.icee.myth.server.index.PlayerIdsOfLevels;
import com.icee.myth.server.levelup.HumanLevelsConfig;
import com.icee.myth.server.message.dbMessage.builder.MapDBMessageBuilder;
import com.icee.myth.server.message.serverMessage.builder.MapMessageBuilder;
import com.icee.myth.utils.Consts;
import com.icee.myth.utils.IdValue;
import com.icee.myth.utils.RandomGenerator;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.TreeMap;

/**
 *
 * @author liuxianke
 */
public class OccupyInfos {
    public static final OccupyInfos INSTANCE = new OccupyInfos();

    public final TreeMap<Integer, OccupyInfo> occupyInfos = new TreeMap<Integer, OccupyInfo>();
    public final LinkedList<OccupyInfo> needSaveOccupyInfos = new LinkedList<OccupyInfo>();

    private OccupyInfos() {
    }

    public OccupyInfo getOccupyInfo(int id) {
        OccupyInfo occupyInfo = occupyInfos.get(id);
        if (occupyInfo != null) {
            occupyInfo.lastVisitTime = GameServer.INSTANCE.getCurrentTime();
        }
        return occupyInfo;
    }

    public OccupyInfo tryGetOccupyInfo(int id, Message waitMessage) {
        OccupyInfo occupyInfo = OccupyInfos.INSTANCE.getOccupyInfo(id);
        if (occupyInfo != null) {
            if (!occupyInfo.inited) {
                occupyInfo.addWaitingMessage(waitMessage);
                return null;
            }
        } else {
            OccupyInfos.INSTANCE.loadOccupyInfoFromDB(id).addWaitingMessage(waitMessage);
            return null;
        }

        return occupyInfo;
    }

    public OccupyInfo loadOccupyInfoFromDB(int id) {
        if (!occupyInfos.containsKey(id)) {
            OccupyInfo occupyInfo = new OccupyInfo(id);
            occupyInfo.lastVisitTime = GameServer.INSTANCE.getCurrentTime();
            occupyInfos.put(id, occupyInfo);

            // 向数据库加载玩家臣属数据
            DBMessageQueue.queue().offer(MapDBMessageBuilder.buildGetCharOccupyInfoDBMessage(id));

            return occupyInfo;
        } else {
            GameLogger.getlogger().log(GameLogMessageBuilder.buildFileDebugGameLogMessage(
                    FileDebugGameLogMessage.DebugLogType.DEBUGLOGTYPE_ERROR,
                    "Load occupy info of player[" + id + "] from db error because in memory."));
        }

        return null;
    }

    public void setNeedSave(OccupyInfo occupyInfo) {
        if (occupyInfo.nextSaveToDBTime == 0) {
            occupyInfo.nextSaveToDBTime = GameServer.INSTANCE.getCurrentTime() + Consts.MILSECOND_5MINITE;  // 5分钟后自动存盘
            needSaveOccupyInfos.add(occupyInfo);
        }
    }

    /**
     * 清理过期没访问的数据
     */
    public void clean(long realCurrTime) {
        for (Iterator<OccupyInfo> it = occupyInfos.values().iterator(); it.hasNext();) {
            OccupyInfo occupyInfo = it.next();

            if (!occupyInfo.inMemory && (realCurrTime - occupyInfo.lastVisitTime >= Consts.CLEAR_OFFLINE_PLAYER_TIME)) {
                assert occupyInfo.nextSaveToDBTime==0;
                if (occupyInfo.fighting) {
                    GameLogger.getlogger().log(GameLogMessageBuilder.buildFileDebugGameLogMessage(
                            FileDebugGameLogMessage.DebugLogType.DEBUGLOGTYPE_ERROR,
                            "***Player[" + occupyInfo.id + "]'s occupy info is fighting when time out.***"));
                } else if (!occupyInfo.inited) {
                    GameLogger.getlogger().log(GameLogMessageBuilder.buildFileDebugGameLogMessage(
                            FileDebugGameLogMessage.DebugLogType.DEBUGLOGTYPE_ERROR,
                            "***Player[" + occupyInfo.id + "]'s occupy info isn't inited when time out.***"));
                } else {
                    it.remove();
                }
            }
        }
    }

    public void saveReachTimeDirtyOccupyInfo(long realCurrTime) {
        for (Iterator<OccupyInfo> iter = needSaveOccupyInfos.iterator(); iter.hasNext(); ) {
            OccupyInfo occupyInfo = iter.next();
            if (occupyInfo.nextSaveToDBTime <= realCurrTime) {
                // save to db
                DBMessageQueue.queue().offer(MapDBMessageBuilder.buildSaveCharOccupyInfoDBMessage(occupyInfo.id, occupyInfo.buildDBOccupyInfoProto()));

                occupyInfo.nextSaveToDBTime = 0;
                iter.remove();
            } else {
                break;
            }
        }
    }

    public void saveDirtyOccupyInfo() {
        for (OccupyInfo occupyInfo : needSaveOccupyInfos) {
                // save to db
                DBMessageQueue.queue().offer(MapDBMessageBuilder.buildSaveCharOccupyInfoDBMessage(occupyInfo.id, occupyInfo.buildDBOccupyInfoProto()));
        }

        needSaveOccupyInfos.clear();
    }

    public BaseTarget[] getTargets(Human human) {
        OccupyInfo occupyInfo = occupyInfos.get(human.id);

        if ((occupyInfo != null) && (occupyInfo.inited)) {
            // 为玩家生成可征服目标列表(可征服的目标不能是君主或已臣服玩家)
            ArrayList<BaseTarget> targets = new ArrayList<BaseTarget>(Consts.MAX_BASE_TARGET_NUM);

            // 1.根据玩家军衔随机选出5级军衔内目标军衔人数分布
            int range = (human.lv<5)?human.lv+4:((human.lv>HumanLevelsConfig.INSTANCE.maxLevel-4)?HumanLevelsConfig.INSTANCE.maxLevel-human.lv+4:9);
            
            // 只查询有玩家的等级表
            ArrayList<IdValue> hasTargetLevels = new ArrayList<IdValue>();
            for (int i=0; i<range; i++) {
                int currentLevel = (human.lv<5)?i+1:human.lv-4+i;
                if (hasTargetsInLevel(human, occupyInfo, currentLevel)) {
                    hasTargetLevels.add(new IdValue(currentLevel, 0));
                }
            }

            // 随机选择目标落在的等级
            int size = hasTargetLevels.size();
            if (size > 0) {
                for (int i = 0; i< Consts.MAX_BASE_TARGET_NUM; i++) {
                    hasTargetLevels.get(RandomGenerator.INSTANCE.generator.nextInt(size)).value++;
                }

                // 2.在选出的对应军衔内选取所需数量的目标
                int currentTargetNum = 0;  // 已有目标数
                for (IdValue idValue : hasTargetLevels) {
                    if (idValue.value > 0) {
                        getTargetsFromLevel(human, occupyInfo, idValue.id, targets, idValue.value);
                        currentTargetNum += idValue.value;

                        if (currentTargetNum >= Consts.MAX_BASE_TARGET_NUM) {
                            break;
                        }
                    }
                }

                if (targets.size() > 0) {
                    BaseTarget[] results = new BaseTarget[targets.size()];
                    results = targets.toArray(results);
                    return results;
                }
            }
        } else {
            GameLogger.getlogger().log(GameLogMessageBuilder.buildFileDebugGameLogMessage(
                    FileDebugGameLogMessage.DebugLogType.DEBUGLOGTYPE_ERROR,
                    "Can't get targets from OccupyInfos because occupyInfo of player[" + human.id + "] not in memory."));
        }

        return null;
    }

    /**
     * 判断等级level中是否有目标，且目标不能为human本人，君主或已臣服玩家
     * @param human
     * @param occupyInfo
     * @param level
     * @return true:有目标 false:无目标
     */
    private boolean hasTargetsInLevel(Human human, OccupyInfo occupyInfo, int level) {
        PlayerIdsOfLevel playerIdsOfLevel = PlayerIdsOfLevels.INSTANCE.getPlayerIdsOfLevel(level);
        if (playerIdsOfLevel != null) {
            return playerIdsOfLevel.hasBaseTarget(human, occupyInfo);
        }

        return false;
    }

    /**
     * 从等级level中选取needNum数量目标放入currentTargetNum下标开始的resulTargets数组中，目标不能为human本人，君主或已臣服玩家
     * @param human
     * @param occupyInfo 
     * @param level
     * @param resultTargets
     * @param currentTargetNum
     * @param needNum
     */
    private void getTargetsFromLevel(Human human, OccupyInfo occupyInfo, int level, ArrayList<BaseTarget> resultTargets, int needNum) {
        PlayerIdsOfLevel playerIdsOfLevel = PlayerIdsOfLevels.INSTANCE.getPlayerIdsOfLevel(level);

        if (playerIdsOfLevel != null) {
            for (int i = 0; i<needNum; i++) {
                if (!playerIdsOfLevel.getBaseTarget(human, occupyInfo, resultTargets)) {
                    return;
                }
            }
        }
    }

    /**
     * 用于调试
     */
    public void printUninitOccupyInfo() {
        LinkedList<Integer> result = new LinkedList<Integer>();
        for (OccupyInfo occupyInfo : occupyInfos.values()) {
            if (!occupyInfo.inited) {
                result.add(occupyInfo.id);
            }
        }

        // 发送计算结果消息给主逻辑线程
        ServerMessageQueue.queue().offer(MapMessageBuilder.buildUninitOccupyInfoMessage(result));
    }

    /**
     * 用于调试
     */
    public void printFightingOccupyInfo() {
        LinkedList<Integer> result = new LinkedList<Integer>();
        for (OccupyInfo occupyInfo : occupyInfos.values()) {
            if (occupyInfo.fighting) {
                result.add(occupyInfo.id);
            }
        }

        // 发送计算结果消息给主逻辑线程
        ServerMessageQueue.queue().offer(MapMessageBuilder.buildFightingOccupyInfoMessage(result));
    }
}
